package com.cskaoyan.javase.oop1._6constructor;

/**
 * 构造器(constructor)
 * 引例:
 * 创建一个教师类，有课程和年龄两个属性，行为是可以上课。
 * 现在我们需要创建以下对象：
 * 18岁的Java老师对象
 * 28岁的C++老师对象
 * 30岁的Python老师对象
 * ...
 * 按照之前我们的做法，需要先创建出对象，再进行成员变量的赋值，这样做如果只有一个两个对象还好，如果需要创建很多对象，就有点过于麻烦了，对象的属性，能不能"出厂"的时候就设定好呢？
 *      言下之意,创建对象过程中,能不能灵活地成员变量赋值呢?
 *      需要使用构造器(也叫构造方法)
 *
 * 构造器的语法:
 * 首先构造器必须写在类体,它是一个特殊的方法.
 * 语法:
 *      [访问权限修饰符] 类名(形参列表){
 *          // 构造方法的方法体
 *      }
 *
 *      解释一下这个语法:
 *          1.访问权限修饰符我们没有学习,这里默认它是public
 *          2.构造器语法中的类名处在普通方法的返回值类型的位置,说明构造方法是没有返回值类型的,构造器也不需要返回值类型
 *              这里的类名可以认为是构造器的名字,构造器的名字和类名保持完全一致
 *              由于类名是大驼峰式的,所以构造器名字也是大驼峰式的
 *          3.形参列表和方法体 与普通方法没有区别,一样使用.
 *
 * 构造器虽然是方法,但它是一个特殊的方法,它的特殊之处体现在:
 *      1.构造方法不是提供给程序员调用的,而是JVM在创建对象过程中,自动调用的
 *          构造器的作用是,在创建对象过程中,完成给成员变量的赋值工作!!!!
 *      2.构造器虽然程序员不可以直接调用,但是我们可以指示JVM创建对象时,用哪个构造器完成成员变量赋值
 *          语法: new 类名(实参列表);
 *          其中new关键字表示创建对象
 *          类名(实参)其实就对应构造器的名字和形参.
 *      3.Java当中,任何一个类,如果没有直接在类中写一个构造器,那么就默认提供一个无参构造,称之为默认无参
 *      格式:
 *      public 类名(){}
 *
 *      默认无参构造器的方法体是空的,是不能给任何成员变量赋值的,所以如果想要用构造器完成成员变量的赋值,需要提供有参构造器.
 *
 * 有参构造器的语法格式:
 *      [访问权限修饰符] 类名(形参列表){
 *          this.成员变量名 = 形参名;
 *          ...
 *      }
 * 构造器虽然不是成员方法,但是构造器中,仍然隐含的有this传参指向当前对象,所以可以用this区分同名局部变量和成员变量
 *
 * 有参构造器和无参构造器,共同组成方法的重载
 * 一个类的构造器之间是可以构成方法重载的
 *
 * 使用有参构造器创建对象,会发现成员变量的取值,最终会显示出构造器中给出的实参的取值.
 * 在Java当中,使用标准格式的构造器给成员变量赋值,它的赋值一定是准确的,成员变量的取值一定是构造器中给出的取值
 * 这是因为 成员变量的赋值方式虽然有很多种,但是永远的最后一步,是构造器.只要是标准格式构造器,那么它的赋值是肯定正确的.
 * 标准格式:
 *      [访问权限修饰符] 类名(形参列表){
 *          this.成员变量名 = 形参名;
 *          ...
 *      }
 *
 * 细节/注意事项:
 *      1.默认无参只有在一个类没有任何构造器时提供.一旦这个类有任何一个构造器,默认无参就没有了.
 *      这里说一个小细节,好的习惯:
 *      建议大家在定义一个可能创建对象的类时,可以顺手提供无参构造器
 *
 *      2.构造器的标准格式形式是很固定的,idea中提供了快速生成类的构造器的方式:
 *        使用快捷键 alt + insert
 *        建议大家日常开发,使用快捷键生成构造器即可
 *
 *      3.类加载和构造器的执行
 *          new对象几次,相应的构造器就会执行几次
 *          但是类加载只有一次,在首次触发类加载时执行
 *
 *      4.类中的多个构造器之间是方法的重载关系,注意重载的条件.尤其是形参名不同不能构造方法重载
 *
 *      5.构造器的作用是在创建对象过程中,给成员变量赋值的.不要认为构造器就是用来创建对象的
 *      创建对象无非就是在内存中新建一种内存模型,它是JVM的事情,和构造器没有关系.
 *      实际上在构造器执行时,对象的结构早就创建完毕了
 *      构造器只是赋值的作用
 *
 *      6.紧跟第五点,构造器的作用就是赋值
 *      所以不要在构造器中乱写代码
 *      构造器最好就是标准格式,不要将业务代码写进去
 *      如果对象成员变量的赋值,确实需要一个复杂的逻辑
 *      建议提取出一个方法,然后在构造器中调用
 *      尽量保证构造器的代码简洁.
 *
 *      7.构造器中允许使用this关键字
 *      除了访问成员变量和方法外,最重要的
 *      还可以用this关键字,去调用自身类中的其他构造器
 *      语法:
 *          this(实参);
 *          表示调用对应形参列表的一个构造器
 *      注意: 在构造器中使用this(实参)表示调用类中其他构造器时,这行代码一定要处在构造器代码的第一行
 *      至于原因,我们先挖个坑,后面再填.
 *
 * @since 16:11
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Teacher t1 = new Teacher();
        // 对象名点成员变量的赋值方式,是在对象创建完毕之后的赋值方式
        t1.course = "Java";
        t1.age = 18;
        Teacher t2 = new Teacher();
        t2.age = 28;
        t2.course = "C++";
        Teacher t3 = new Teacher();
        t3.age = 38;
        t3.course = "Python";

        Teacher t4 = new Teacher("数学", 48);
        System.out.println(t4.age);
        System.out.println(t4.course);

        System.out.println("-----------------");
        Teacher t5 = new Teacher("物理", 38, "李婷");
        System.out.println(t5.course);
        System.out.println(t5.age);
        System.out.println(t5.name);
    }
}

class Teacher {
    String course = "语文";
    int age = 29;
    String name;

    public void teach() {
        System.out.println(age + "的老师,正在教授" + course);
    }

    // 写一个三参构造器,给所有成员变量赋值
    public Teacher(String course, int age, String name) {
        this(course, age);
        this.name = name;
        // Call to 'this()' must be first statement in constructor body
        // this(course, age);
    }

    // 默认无参
    public Teacher() {
        System.out.println("默认无参执行了!");
    }

    // 双参构造器 给course和age赋值
    public Teacher(String course, int age) {
        this.course = course;
        this.age = age;
    }

    public Teacher(String name) {
        // 给name赋值需要很复杂的逻辑
        // 或者创建对象,就必须执行一段逻辑
        init();
    }

    public void init() {
        // 逻辑代码
    }

    // public Teacher(String course) {
    //     this.name = name;
    // }
}